﻿using System;

namespace AntBot
{
	public class Torus
	{
		private readonly Square[,] map;
		public readonly int Rows;
		public readonly int Cols;

		public Torus(int cols, int rows)
		{
			Cols = cols;
			Rows = rows;
			map = new Square[cols, rows];
		}

		public void Set(int col, int row, Square square)
		{
			map[AdjustCol(col), AdjustRow(row)] = square;
		}

		public void Set(Location location, Square square)
		{
			Location adjustedLocation = Adjust(location);
			map[adjustedLocation.Col, adjustedLocation.Row] = square;
		}

		public void SetWater(int col, int row)
		{
			map[AdjustCol(col), AdjustRow(row)].IsWater = true;
		}

		public void SetFood(int col, int row, bool isFood)
		{
			map[AdjustCol(col), AdjustRow(row)].IsFood = isFood;
		}

		public void SetAnt(int col, int row, int owner)
		{
			map[AdjustCol(col), AdjustRow(row)].Ant = owner;
		}

		public void SetLastSeen(int col, int row, int turn)
		{
			map[AdjustCol(col), AdjustRow(row)].LastSeen = turn;
		}

		public Square Get(int col, int row)
		{
			return map[AdjustCol(col), AdjustRow(row)];
		}

		public Square Get(Location location)
		{
			Location adjustedLocation = Adjust(location);
			return map[adjustedLocation.Col, adjustedLocation.Row];
		}

		public static int Adjust(int value, int maxValue)
		{
			if (value < maxValue && value >= 0)
				return value;

			value = value % maxValue;
			if (value < 0) value = maxValue + value;
			return value;
		}

		private int AdjustCol(int col)
		{
			return Adjust(col, Cols);
		}

		private int AdjustRow(int row)
		{
			return Adjust(row, Rows);
		}

		private Location Adjust(Location location)
		{
			return new Location(AdjustCol(location.Col), AdjustRow(location.Row));
		}

		public static Location Adjust(Location location, int cols, int rows)
		{
			return new Location(Adjust(location.Col, cols), Adjust(location.Row, rows));
		}

		public static int DV(int value1, int value2, int maxValue)
		{
			int dv = Math.Abs(Adjust(value1, maxValue) - Adjust(value2, maxValue));
			return Math.Min(dv, maxValue - dv);
		}

		public int ManhattanDistance(Location loc1, Location loc2)
		{
			return ManhattanDistance(loc1.X, loc1.Y, loc2.X, loc2.Y, Cols, Rows);
		}

		public static int ManhattanDistance(int c1, int r1, int c2, int r2, int cols, int rows)
		{
			return DV(c1, c2, cols) + DV(r1, r2, rows);
		}

		public int ManhattanDistance(int c1, int r1, int c2, int r2)
		{
			return ManhattanDistance(c1, r1, c2, r2, Cols, Rows);
		}

		public static int ManhattanDistance(Location loc1, Location loc2, int cols, int rows)
		{
			return ManhattanDistance(loc1.X, loc1.Y, loc2.X, loc2.Y, cols, rows);
		}

		public static int Distance2(Location loc1, Location loc2, int cols, int rows)
		{
			return Distance2(loc1.Col, loc1.Row, loc2.Col, loc2.Row, cols, rows);
		}

		public static int Distance2(int c1, int r1, int c2, int r2, int cols, int rows)
		{
			return (int)(Math.Pow(DV(c1, c2, cols), 2) + Math.Pow(DV(r1, r2, rows), 2));
		}

		public int Distance2(Location loc1, Location loc2)
		{
			return Distance2(loc1.Col, loc1.Row, loc2.Col, loc2.Row, Cols, Rows);
		}

		public int Distance2(int c1, int r1, int c2, int r2)
		{
			return Distance2(c1, r1, c2, r2, Cols, Rows);
		}

		public double Distance(Location loc1, Location loc2)
		{
			return Math.Sqrt(Distance2(loc1.Col, loc1.Row, loc2.Col, loc2.Row, Cols, Rows));
		}

		public double Distance(int c1, int r1, int c2, int r2)
		{
			return Math.Sqrt(Distance2(c1, r1, c2, r2, Cols, Rows));
		}

		public void Reset()
		{
			for (int i = 0; i < Cols; i++)
				for (int j = 0; j < Rows; j++)
					map[i, j].Reset();
			
		}
	}
}
